package com.ruleengine.instruction;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;

import com.ruleengine.executer.InstructionExecuter;
import com.ybt.configration.IYBTConfigration;
import com.ybt.configration.YBTConfigration;
import com.ybt.configration.YBTRuntimeConfiguration;
import com.ybt.pub.XmlUtil;

public class InstructionManager {
    private static String CONFIG_DIR = "conf"
    + File.separator + "instructionDefine.xml";
    private volatile static InstructionManager manager;
    private Map<InstructionType, InstructionExecuter<Instruction>> executers;
    private Map<InstructionType, InstructionEntry> configs;
    private InstructionManager(){
        executers = new HashMap<InstructionType, InstructionExecuter<Instruction>>();
        configs = new HashMap<InstructionType, InstructionEntry>();
        
        CONFIG_DIR = YBTConfigration.getInstance().getProperty(
                IYBTConfigration.WORKFLOW_DEPLOY_PATH)
                + File.separator + CONFIG_DIR;

        loadFromLocal();
    }
    public static InstructionManager getInstance(){
        if(manager == null){
            synchronized(InstructionManager.class){
                if(manager == null){
                    manager = new InstructionManager();
                }
            }
        }
        return manager;
    }
    
    public <E extends Instruction> E createInstruction(InstructionType instType){
        InstructionEntry entry = configs.get(instType);
        if(entry==null){
            throw new RuntimeException("unsupport instruction type, type is = " + instType);
        }
        try {
            Class c = Class.forName(entry.impl);
            return (E)c.newInstance();
        } catch (Exception e) {
            // TODO Auto-generated catch block
           throw new RuntimeException("initionlize instruction error, the type is " + entry.impl);
        }
    }
    
    public InstructionExecuter getInstructionExecuter(InstructionType instType){
        InstructionExecuter<Instruction> execute = executers.get(instType);
        if(execute==null){
            throw new RuntimeException("unsupport Instruction Executer, type is ="+instType);
        }
        return execute;
    }
    
    private void loadFromLocal() {

        File file = new File(CONFIG_DIR);
        if (!file.exists()) {
            throw new RuntimeException("can't find the configration file : "
                    + CONFIG_DIR);
        }

        try {
            Document doc = XmlUtil.loadXml(CONFIG_DIR,
                    YBTRuntimeConfiguration.getInstance()
                            .getConfigFileCharset());
            List<Element> tasks = doc.getRootElement().elements("instruction");
            for (Element et : tasks) {
                String type = et.attributeValue("type");
                String impl = et.element("implement").getTextTrim();
                String executer = et.element("executer").getTextTrim();
                InstructionType ttype = InstructionType.valueOf(type);
                
                this.configs.put(ttype, new InstructionEntry(ttype, impl, executer));
                try {
                    // 初始化执行器
                    Class c = Class.forName(executer);
                    InstructionExecuter<Instruction> p = (InstructionExecuter<Instruction>) c
                            .newInstance();
                    executers.put(ttype, p);
                } catch (Exception e) {
                    throw new RuntimeException(
                            "initialize instruction executer error :"
                                    + executer);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("parse configration file error : "
                    + CONFIG_DIR, e);
        }
    }
    
    class InstructionEntry{
        InstructionType type;
        String impl;
        String executer;
        
        public InstructionEntry(InstructionType type, String impl,
                String executer) {
            super();
            this.type = type;
            this.impl = impl;
            this.executer = executer;
        }
        
        
    }

}
